// test_cgi.cc
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include "../Log.hpp"

using namespace std;

bool GetQueryString(std::string &query_string)
{
    bool result=false;
    // 从环境变量中获得请求方法（子进程在程序替换之前写好了）
    std::string method = getenv("METHOD");
    // cerr << "Debug Test : " << getenv("METHOD") << endl;
        // 为什么这里要用cerrc测试？
            // 原因是因为当子进程将程序替换成这份代码时，子进程文件打开表中的fd=0和fd=1已经被重定向到管道文件中了
            // 所以如果我们继续用cout标准输出，后面的语句会被输出到管道文件中，我们没有办法在命令行中观测到
            // 而cerr对应的fd=2目前还没有被输出重定向，我们还能在屏幕上观测到
    if("GET" == method){
        // 如果请求方法是method，那么 
        query_string = getenv("QUERY_STRING");
        // cerr << "Debug QUERY_STRING: " << query_string << endl; // 测试代码
        result=true;
    }
    else if("POST" == method){
        // CGI函数如何得知需要从标准输入流中读取多少个字节的数据呢？
            // 通过环境变量告知
        int content_length = atoi(getenv("CONTENT_LENGTH"));//调用atoi函数，将环境变量中字符串类型的content_length转化为整形           
        // cerr << "Debug CONTENT_LENGTH: " << content_length << endl;
        
        // 前面父进程会把正文部分写到管道文件中，下面我们就从管道文件中，将正文中的数据读取出来，存到当前进程的query_string中，以便后续对数据进行处理
        char c = 0;
        while(content_length){
            read(0, &c, 1);// 从fd=0对应的文件中读取1个字符，读到变量c中，注意：这里fd=0经过重定向之后，已经不再是标准输入流，对应的文件也不再是键盘，而是output管道的读端
            query_string.push_back(c);
            content_length--;
        }
        // 到此为止我们就完成了对POST请求报文中用户上传数据的读取
        // cerr << "Post Debug QUERY_STRING: " << query_string << endl; 测试代码
        result=true;
    }
    return result;
}

bool CutString(const std::string &target, std::string sep, std::string &sub1_out, std::string &sub2_out)
{
    size_t pos = target.find(sep);
    if(pos != std::string::npos){
        sub1_out = target.substr(0, pos); 
        sub2_out = target.substr(pos+sep.size());
        return true;
    }
    return false;
}

// 这是一份测试环境变量传参的代码
// 这个函数其实就是子进程发生进程替换之后，换上去的新代码
// 也就是说ProcessCgi中的子进程执行完execl语句之后，就执行下面的代码
int main()
{
    
    std::string query_string;
    GetQueryString(query_string);
    //协议规定，浏览器上传上来的数据格式都是这样的：a=100&b=200 

    // 我们先按&进行拆分
    std::string str1;
    std::string str2;
    CutString(query_string, "&", str1, str2);

    // 再按照=进行查分，把变量的name和value进行分离
    std::string name1;
    std::string value1;
    CutString(str1, "=", name1, value1);

    std::string name2;
    std::string value2;
    CutString(str2, "=", name2, value2);

    //1 这里cout语句是将内容输出到fd=1对应的文件中，以前fd=1对应的是显示屏，重定向之后对应的是管道文件input的写端
    std::cout << name1 << " : " << value1 << std::endl;
    std::cout << name2 << " : " << value2 << std::endl;
    //2 这里cerr语句是将内容输出到fd=2对应的文件中，fd=2没有重定向，对应的仍然是显示屏
    std::cerr << name1 << " : " << value1 << std::endl;
    std::cerr << name2 << " : " << value2 << std::endl;

    int x = atoi(value1.c_str());
    int y = atoi(value2.c_str()); 
    std::cout << "<html>";
    std::cout << "<head><meta charset=\"utf-8\"></head>";
    std::cout << "<body>";
    std::cout << "<h3>" << value1 << " + " << value2 << " = " << x+y << "";
    std::cout << "<h3>" << value1 << " - " << value2 << " = " << x-y << "";
    std::cout << "<h3>" << value1 << " * " << value2 << " = " << x*y << "";
    std::cout << "<h3>" << value1 << " / " << value2 << " = " << x/y << "";
    std::cout << "</body>";
    std::cout << "</html>";
    return 0;
}
